﻿#include "VulkanImageWidget.h"
#include "Render/VulkanContext.h"
#include "Render/Widget/VulkanWindow.h"
#include "Render/PreRender/PreRenderTexture2D.h"
#include "Render/Texture/VulkanTexture2D.h"

CVulkanImageWidgets::~CVulkanImageWidgets()
{
    //销毁command buffer
    CVulkanContext::Get()->GetRecycleObjects()->CommandBuffers.Add(CommandBuffer);
}

void CVulkanImageWidgets::PreProcess(const TVector<SPreRenderImageWidget> &AllImageData)
{
    TVector<SPreRenderImageWidget> ValidData;
    ValidData.Reserve(AllImageData.Num());

    for (const auto& ImageData : AllImageData)
    {
        //检查数据是否合法
        if(!ImageData.Image)
        {
            continue;
        }

        if(ImageData.Viewport.width<=0 || ImageData.Viewport.height<=0)
        {
            continue;
        }

        //检查裁剪是否合法
        if(ImageData.Scissor.extent.width<=0 || ImageData.Scissor.extent.height<=0)
        {
            continue;
        }

        //如果裁剪矩形完全在视口外面，则不需要绘制
        if(ImageData.Scissor.offset.x+ImageData.Scissor.extent.width<=ImageData.Viewport.x ||
            ImageData.Scissor.offset.x>=ImageData.Viewport.x+ImageData.Viewport.width ||
            ImageData.Scissor.offset.y+ImageData.Scissor.extent.height<=ImageData.Viewport.y ||
            ImageData.Scissor.offset.y>=ImageData.Viewport.y+ImageData.Viewport.height)
        {
            continue;
        }

        ValidData.Add(ImageData);

    };

    //创建渲染对象
    ImageWidgets.Resize(ValidData.Num());
    for (int i = 0; i < ValidData.Num(); ++i)
    {
        ImageWidgets[i].BindData(ValidData[i]);
    }

    //统计描述符
    SVulkanDescriptorPoolSize PoolSize;
    PoolSize.SetNum= ImageWidgets.Num();
    PoolSize+=CVulkanContext::Get()->GetImageWidgetDescriptorSetLayout().DescriptorNums*ImageWidgets.Num();
    CVulkanContext::Get()->GetAutoResetDescriptorPool()->GenerallPool.PoolSize+=PoolSize;



}

void CVulkanImageWidgets::CreateSelfRecordTasks(CVulkanWindow *InRenderSurface, SRenderCommandBuffers &OutCmdBuffersToExecute)
{
    if(ImageWidgets.Num()==0)
    {
        return;
    }

    //创建command buffer
    if(!CommandBuffer)
    {
        //allocate a command buffer for this widget
        vk::CommandBufferAllocateInfo CommandBufferAllocateInfo=vk::CommandBufferAllocateInfo()
            .setCommandPool(CVulkanContext::Get()->GetRenderThreadCommandPool())
            .setLevel(vk::CommandBufferLevel::eSecondary)
            .setCommandBufferCount(1);

        CommandBuffer = CVulkanContext::Get()->GetDevice().allocateCommandBuffers(CommandBufferAllocateInfo)[0];
    }


    OutCmdBuffersToExecute.ImageWidgetRenderPassCommandBuffers.Add(CommandBuffer);

    vk::CommandBufferInheritanceInfo CommandBufferInheritanceInfo=vk::CommandBufferInheritanceInfo()
        .setRenderPass(CVulkanContext::Get()->GetWidgetRenderPass())
        .setFramebuffer(InRenderSurface->GetCurrentFrameBuffer())
        .setSubpass(0)
    ;

    vk::CommandBufferBeginInfo CommandBufferBeginInfo=vk::CommandBufferBeginInfo()
        .setFlags(vk::CommandBufferUsageFlagBits::eRenderPassContinue)
        .setPInheritanceInfo(&CommandBufferInheritanceInfo);

    CommandBuffer.begin(CommandBufferBeginInfo);
    //绑定pipeline
    CommandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics,CVulkanContext::Get()->GetImageWidgetPipeline());    

    for (auto& ImageWidget : ImageWidgets)
    {
        ImageWidget.Draw(CommandBuffer);
    }

    CommandBuffer.end();

}

void CVulkanImageWidget::BindData(const SPreRenderImageWidget& InData)
{
    Depth=InData.Depth;
    Viewport=InData.Viewport;
    Scissor=InData.Scissor;
    Image=CVulkanContext::Get()->ToVulkanTexture2D(InData.Image->Pixels);
    LeftTopUV=InData.LeftTopUV;
    RightBottomUV=InData.RightBottomUV;
    TintColor=InData.TintColor;
}

void CVulkanImageWidget::Draw(vk::CommandBuffer CommandBuffer)
{
    //推送常量
    CVulkanContext::SImageWidgetVertexPushConstant PushConstantValue;
    PushConstantValue.ZOrder=Depth;
    PushConstantValue.LeftTopUV[0]=LeftTopUV.X;
    PushConstantValue.LeftTopUV[1]=LeftTopUV.Y;
    PushConstantValue.RightBottomUV[0]=RightBottomUV.X;
    PushConstantValue.RightBottomUV[1]=RightBottomUV.Y;
    TintColor.ToFloatArray(PushConstantValue.TintColor);
                    
    CommandBuffer.pushConstants(
        CVulkanContext::Get()->GetImageWidgetPipelineLayout()
        ,vk::ShaderStageFlagBits::eVertex
        ,0
        ,sizeof(CVulkanContext::SImageWidgetVertexPushConstant)
        ,&PushConstantValue);


    CommandBuffer.setViewport(0,1,&Viewport);
    CommandBuffer.setScissor(0,1,&Scissor);
                       
    //创建并绑定描述符集
    {

        //创建描述符集
        TVector<vk::DescriptorSetLayout> DescriptorSetLayouts=
        {
            CVulkanContext::Get()->GetImageWidgetDescriptorSetLayout().Layout
        };

        vk::DescriptorSetAllocateInfo DescriptorSetAllocateInfo = vk::DescriptorSetAllocateInfo()
            .setDescriptorPool(CVulkanContext::Get()->GetAutoResetDescriptorPool()->GenerallPool.Pool)
            .setSetLayouts(DescriptorSetLayouts);

        auto DescriptorSet = CVulkanContext::Get()->GetDevice().allocateDescriptorSets(DescriptorSetAllocateInfo)[0];
    
        //写入描述符
        vk::DescriptorImageInfo DescriptorImageInfo = vk::DescriptorImageInfo()
            .setSampler(CVulkanContext::Get()->GetDefaultImageSampler())
            .setImageView(Image->ImageView)
            .setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);

        vk::WriteDescriptorSet WriteDescriptorSet = vk::WriteDescriptorSet()
            .setDstSet(DescriptorSet)
            .setDstBinding(0)
            .setDstArrayElement(0)
            .setDescriptorType(vk::DescriptorType::eCombinedImageSampler)
            .setDescriptorCount(1)
            .setPImageInfo(&DescriptorImageInfo);

        CVulkanContext::Get()->GetDevice().updateDescriptorSets(WriteDescriptorSet, nullptr);
    
        //绑定描述符集
        CommandBuffer.bindDescriptorSets(
            vk::PipelineBindPoint::eGraphics
            ,CVulkanContext::Get()->GetImageWidgetPipelineLayout()
            ,0
            ,1
            ,&DescriptorSet
            ,0
            ,nullptr);
    }    


    CommandBuffer.draw(4,1,0,0);    
}
